/*
 * Project: Game NinetyNine
 * Author:Team JOKE
 * Date: Nov. 8
 * Version: 1.0
 * Description: This is the Singleton bean for Game NinetyNine.
 *              Game logic included here.
 */
package com.server;

import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.ObjectMessage;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.Topic;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.NoResultException;
import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import NinetyNine.*;
import java.text.DecimalFormat;
import java.util.Enumeration;

@Singleton
public class NinetyNineGameBean implements NinetyNineGameBeanRemote {

    @PersistenceContext(unitName = "NinetyNinePU")
    private EntityManager em;
    @Resource(name = "jms/gameStateTopic")
    private Topic gameStateTopic;
    @Resource(name = "jms/gameStateConnectionFactory")
    private ConnectionFactory gameStateConnectionFactory;
    boolean playing = false; // whether or not a game is being played
    boolean firstTurn = true;//whether it is the first turn of the player
    int currentPlayer = 0; // current player in activePlayers
    int total = 0; // running total in game. should never exceed 99
    int order = 0; // +1 means forwards, -1 means backwards
    Card lastCardPlayed = null; // Card object of the most recent card played
    int count = 0; // counts the number of cards the player has played
    int turn = 0; // number of turns that have passed since the game started
    Hashtable<Integer, Player> players = new Hashtable(); // logged in players
    ArrayList<Player> activePlayers = null; // list of active players
    Deck drawDeck = null; // deck that players draw from
    Deck garbageDeck = null; // garbage deck, shuffle and add to 
    Pattern regex = Pattern.compile("[a-zA-Z0-9]+"); // pattern for validation
    NinetyNineMessage message = new NinetyNineMessage(); // Message for JMS
    ScoreBoard scores = null; // scores that are sent to the leaderboard UI
    Player admin = null; // special admin player
    PlayerEntity results = null; // Entity object that is used to access DB
    MessageDigest hash = null; // object to hash password

    //JMS message
    /**
     * Create a JMS Message using the NinetyNineMessage object. Prepares it
     * for sending to the clients.
     */
    private void createMessage() {
        message.setTotal(total);

        // Set values for the JMS message
        if (activePlayers != null) {
            message.setTurn(turn);
            message.setPlaying(playing);
            message.setNumActive(activePlayers.size());
            message.setCurrentPlayer(activePlayers.get(
                    currentPlayer).getUsername());
            message.setCurrentUID(activePlayers.get(currentPlayer).getUserid());
            message.getActiveUsernames().clear();
            for (int i = 0; i < activePlayers.size(); i++) {
                message.getActiveUsernames().add(
                        activePlayers.get(i).getUsername());
            }
        } else { // if activePlayers is null, clear the message
            message.getActiveUsernames().clear();
            message.setNumActive(0);
            message.setCurrentPlayer(null);
        }

        // fill the rest of the usernames with "No Player" strings
        while (message.getActiveUsernames().size() < 5) {
            message.getActiveUsernames().add("No Player");
        }
        message.setLastCardPlayed(lastCardPlayed);
    }

    // send JMS Message to all subscribed clients (Netbeans generated)
    private void sendJMSMessageToGameStateTopic(Object messageData)
            throws JMSException {
        Connection connection = null;
        Session session = null;
        ObjectMessage msg = null;
        try {
            connection = gameStateConnectionFactory.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer messageProducer =
                    session.createProducer(gameStateTopic);
            msg = session.createObjectMessage((Serializable) messageData);
            messageProducer.send(msg);
        } finally {
            if (session != null) {
                try {
                    session.close();
                } catch (JMSException e) {
                    Logger.getLogger(this.getClass().getName()).log(
                            Level.WARNING, "Cannot close session", e);
                }
            }
            if (connection != null) {
                connection.close();
            }
        }
    }

    // Game Logic
    /**
     * Deals cards to the activePlayers
     */
    private void deal() {
        for (int num = 0; num < 5; num++) {
            for (int i = 0; i < activePlayers.size(); i++) {
                activePlayers.get(i).getHand().add(drawDeck.draw());
            }
        }
    }

    /**
     * Checks to see if there is a winner, if there is, prepare the game state
     * and the JMS message to notify the players that the game is over.
     */
    private void checkWinner() {

        // if there is one player left, declare winner! and set JMS values
        if (activePlayers != null && activePlayers.size() == 1) {
            total = 0;
            turn = 0;
            playing = false;
            message.setCurrentPlayer(null);
            Player winner = activePlayers.remove(0);
            garbageDeck.stack(winner.getHand());
            drawDeck.stack(garbageDeck);
            winner.setUserstatus(0);
            winner.getHand().empty();
            message.setWinner(winner.getUsername());
            winner.won();
            saveWon(winner.getUsername(), winner.getWins(), winner.calcRatio());
            save();
            activePlayers = null;
        } else if (activePlayers == null || activePlayers.size() == 0) {
            total = 0;
            turn = 0;
            playing = false;
            message.setCurrentPlayer(null);
            activePlayers = null;
        }
    }

    /**
     * Returns an int indicating whether registration was successful or not.
     * 1 = username or password was null or blank or username too long
     * 2 = username contains illegal characters
     * 3 = password isn't the right length
     * 4 = username already exists in the database
     *
     * @param username is the name of the new user
     * @param password is their password
     *
     * @return the return code indicating the success of registration
     */
    public int register(String username, String password) {
        // Check to see if username exists or password exists
        if (username == null || username.equals("") || password == null) {
            return 1;
        }
        if (username.length() > 20) {
            return 1; // username must be less than 20 characters
        } else if (!regex.matcher(username).matches()) {
            return 2; // illegal characters in username
        } else if (password.length() < 6 || password.length() > 20) {
            return 3; // password isn't between 6 and 20 characters
        } else if (findWithName(username) != null) {
            return 4; // username already exists in database
        }

        // Create the hashing function
        try {
            hash = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        // Hash the password
        String hashPass = Base64.encodeBytes(hash.digest(password.getBytes()));
        hash.reset();

        // check database for number of registered players.
        int UID = count(); // should be number of registered players + 1

        // Create a new player
        Player temp = new Player(UID, username, 0, 0, 0, 0);
        players.put(new Integer(UID), temp);
        results = new PlayerEntity(new Integer(UID));
        results.setUsername(username);
        results.setPassword(hashPass);
        results.setGameplayed(new Integer(0));
        results.setGamewon(new Integer(0));
        results.setGameratio(new Double(0.0));
        create(results); // save it to the database
        password = null;
        return 0;
    }

    /**
     * Returns an int indicating whether login was successful or not.
     * -1 means login failed
     * any positive number means it was successful and the number is the user's
     * UID.
     *
     * @param username is the name of the new user
     * @param password is their password
     *
     * @return the return code indicating the success of login
     */
    public int login(String username, String password) {
        results = findWithName(username);

        try {
            hash = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        String hashPass = Base64.encodeBytes(hash.digest(password.getBytes()));
        hash.reset();
        password = null;

        // Check if player credentials are valid
        if (results != null && results.getUsername().equals(username)
                && results.getPassword().equals(hashPass)) {
            if (username.equals("admin") && admin==null) {
                admin = new Player(0, username, 0, 0, 0, 0);
                return 0;
            } else if(players.get(results.getId())==null){

                players.put(new Integer(results.getId()), new Player(
                        results.getId(), username, 0, results.getGameplayed(),
                        results.getGamewon(), results.getGameratio()));
                createMessage();

                // send JMS Message
                try {
                    sendJMSMessageToGameStateTopic(message);
                } catch (JMSException e) {
                    e.printStackTrace(System.err);
                }
                return results.getId();
            }
        }
        return -1;
    }

    /**
     * Return a card object of hand card
     * initialize the index to be 0
     * check if game has started
     * check if # of activeplayers is 2-5
     * check if # of activeplayers is smaller than # of players.
     * check if index is within the range of # of activeplayers.
     * once finished, go to the next player.
     *
     * @param UID specify the UID of player.
     * 
     * @return card object with matching UID
     */
    public Deck getHand(int UID) {
        int index = 0;
        while (playing == true && //check if game has started
                activePlayers.size() <= 5 && //check if # of activeplayers is 2-5
                activePlayers.size() >= 2
                && activePlayers.size() <= players.size()//check if # of activeplayers is smaller than # of players.
                && index < activePlayers.size()) {//within the range of # of activeplayers
            if (activePlayers.get(index).getUserid() == UID) {
                return (activePlayers.get(index)).getHand();//call getHand()method
            }
            index++;//next player
        }
        return players.get(new Integer(UID)).getHand();

    }

    /**
     * Return a value indicating a player is ready or not.
     * Initialize retVal to be 0
     * Check if there is no activeplayers, create a new Arraylist,
     * reset JMS message then return 2.
     * Check if no game is running and the # of activeplayers,
     * set users status and create JMS message
     *
     * @param UID specify the UID of player.
     * 
     * @return the value representing ready status.
     */
    public int ready(int UID) {
        Integer temp = new Integer(UID);
        int retVal = 0;
        if (activePlayers == null) {//if there is no activeplayers, the return 2.
            activePlayers = new ArrayList();// create a new Arraylist
            message.reset();//reset JMS message
            retVal = 2;
        }

        if (playing == false && // check if no game is running
                activePlayers.size() < 5 && //check the # of activeplayers
                activePlayers.add(players.get(temp))) {
            players.get(temp).setUserstatus(1);//set users status
            createMessage();//create JMS message

            // send JMS Message
            try {
                sendJMSMessageToGameStateTopic(message);
            } catch (JMSException e) {
                e.printStackTrace(System.err);
            }
            if (retVal != 2) {//check the value of relVal
                retVal = 1;
            }
        } else {
            retVal = 0;
        }

        return retVal;//return retVal
    }

   /**
    * Called to initialize the game
    * if number of player is 2-5,then it is successfully start the game
    * 
    * @return boolean shows whether it starts the game successfully
    */
    public boolean startGame() {
        if (activePlayers != null && activePlayers.size() <= 5
                && activePlayers.size() >= 2) {//number of player should be 2-5

            //Instialize the states of the game
            drawDeck = new Deck(true);//create drawDeck
            garbageDeck = new Deck();//create garbageDeck
            order = 1;//player order is forward
            turn = 0;//number of turn initail
            total = 0;//total num for played card is zero
            firstTurn = true;//first turn of playing
            playing = true;//player is playing
            drawDeck.shuffle(5);//draw five cards from Deck
            deal();//deal cards to player
            lastCardPlayed = drawDeck.draw();//card be played latest
            Player tempPlayer = null;//initial temp player

           //call the playNinetyNine functino to play game
           playNinetyNine(lastCardPlayed, true);
           for (int temp = 0; temp < activePlayers.size(); temp++) {

                tempPlayer = activePlayers.get(temp);
                tempPlayer.played();
                //update and save the player info in db
                savePlayed(tempPlayer.getUsername(),
                        tempPlayer.getPlays(), tempPlayer.calcRatio());
                save();
            }

            // create and send JMS Message
            createMessage();
            try {
                sendJMSMessageToGameStateTopic(message);
            } catch (JMSException e) {
                e.printStackTrace(System.err);
            }

            return true;//Start successfully
        } else {
            return false;//fail to start
        }
    }

    /**
     * Called when a card is played. Contains all of the gameplay logic.
     * If the card is a special card, it will take the appropriate actions.
     * If the card is a normal card, it will at the value to the total.
     * If the card is successfully played, a new card is returned (drawn).
     * If the card fails to be played, the same card is returned.
     * If the played fails to play a valid card 3 times, they lose and a null
     * is returned.
     *
     * @param played is the Card object that was played
     * @param add is a boolean indicating whether a 10 or Queen should be added
     * or subtracted from the total.
     *
     * @return a Card object indicating whether the card was 
     * played successfully.
     */
    public Card playNinetyNine(Card played, boolean add) {
        Deck hand = null;
        Card drawn = null;
        if (played != null && played.isValid() && playing && activePlayers != null) {
            count++;

            // Play the card and check the value.
            int temp = total;
            if (played.getValue() == 13) { // if card is a King, set total
                temp = 99;
            } else if (played.getValue() == 12) { // if Queen, add/subtract 20
                temp = add ? total + 20 : total - 20;
            } else if (played.getValue() == 11) { // if Jack, skip turn
                temp = total;
                if (activePlayers.size() != 2) {
                    currentPlayer = (activePlayers.size()
                            + currentPlayer + 2 * order) % activePlayers.size();
                } else {
                    currentPlayer = (activePlayers.size()
                            + currentPlayer + 1 * order) % activePlayers.size();
                }
            } else if (played.getValue() == 10) { // if 10, add/subtract 10
                temp = add ? total + 10 : total - 10;
            } else if (played.getValue() == 4) { // if 4, reverse order
                temp = total;
                order = -1*order;
            } else { // otherwise, add the value of the card to the total
                temp = total + played.getValue();
            }

            // check if the total went over 99, if it did, return the same card.
            if (temp > 99) {
                if (count >= 3) {
                    iQuit();
                    return null;
                }
                return played;
            }

            count = 0;
            if (firstTurn) {
                firstTurn = false;
                total = temp;
            } else {
                turn++;
                // Update hand of player and draw card
                hand = activePlayers.get(currentPlayer).getHand();
                hand.remove(played);
                drawn = drawDeck.draw();
                hand.add(drawn);
                lastCardPlayed = played;

                // Check to see if drawDeck is empty
                if (drawDeck.size() == 0) {
                    garbageDeck.shuffle(5);
                    Deck tempDeck = drawDeck;
                    drawDeck = garbageDeck;
                    garbageDeck = tempDeck;
                }

                // set next player
                total = temp;
                if (played.getValue() != 11) {
                    currentPlayer = (activePlayers.size()
                            + currentPlayer + order) % activePlayers.size();
                }
            }

            // create a JMS message to update the game state
            createMessage();

            // send JMS Message
            try {
                sendJMSMessageToGameStateTopic(message);
            } catch (JMSException e) {
                e.printStackTrace(System.err);
            }
        }
        return drawn;
    }

   /**
    * called when player gives up
    * if number of player is 2-5,then it is successfully quit the game
    * remove the player on the activePlayer list
    * 
    * @return boolean shows whether it quits the game successfully
    */
    public boolean iQuit() {
        if (activePlayers != null && activePlayers.size() >= 2
                && activePlayers.size() <= 5) {//number of player should be 2-5

            //remove player from the active player list
            Player remove = activePlayers.remove(currentPlayer);
            remove.setUserstatus(0);
            if (order == -1) {//order is backward
                if (currentPlayer != 0) {//Player not in activeplayer list
                    currentPlayer += order;//get player position
                } else {//Player in the activeplayer list
                    currentPlayer = activePlayers.size() - 1;
                }
            } else if (currentPlayer == activePlayers.size()) {
                currentPlayer = 0;//player is first in the activeplayer list
            }

            //clear the cards in the player's hand
            garbageDeck.stack(remove.getHand());
            remove.getHand().empty();

            //Check who is the winner
            checkWinner();

            //Create and send JMS message
            createMessage();
            message.setLoser(remove.getUsername());
            try {
                sendJMSMessageToGameStateTopic(message);
            } catch (JMSException e) {
                e.printStackTrace(System.err);
            }
            message.setWinner(null);
            message.setLoser(null);

            return true;// quit successfully
        } else {
            return false;// cannot quit
        }

    }

    /**
     * Logs the player with given UID out of the system. Only works when
     * the player isn't in a game.
     *
     * @param UID is the UID of the player to be logged out
     * @return true or false showing whether or not it was successful.
     */
    public boolean logout(int UID) {
        if (UID == 0) {
            admin = null;
            return true;
        }
        Player remove = players.get(new Integer(UID));
        if (remove.getUserid() == UID) {
            players.remove(new Integer(UID));
            if (activePlayers != null) {
                activePlayers.remove(remove);
            }
            return true;
        }
        return false;
    }

    /**
     * Forcefully removes a player from play using their UID. This method
     * will remove a player regardless of whether they're in a game or if
     * they just logged in. It returns true or false depending on whether it
     * succeeded or not.
     *
     * @param UID is the user ID of the player to be removed.
     *
     * @return True for successful removal, false for a failed removal.
     */
    public boolean destroyPlayer(int UID) {
        Integer IntUID = new Integer(UID);
        Player destroy = players.get(IntUID);
        if (destroy == null) {
            return false;
        }
        // Update current player and remove player from activePlayers
        if (destroy.getUserstatus() == 1) { // if user is playing..
            if (activePlayers.size() > 1) { // if there is more than one player
                // Update the current player based on the order
                if (destroy.equals(activePlayers.get(currentPlayer))) {
                    if (order == 1) { // if going clockwise
                        currentPlayer = (currentPlayer + order)
                                % activePlayers.size();
                    } else { // if going counterclockwise
                        if (currentPlayer != 0) {
                            currentPlayer += order;
                        } else {
                            currentPlayer = activePlayers.size() - 2;
                        }
                    }
                } else if (currentPlayer > activePlayers.indexOf(destroy)) {
                    currentPlayer--;
                }
            }
            activePlayers.remove(destroy);
            if (garbageDeck != null) { // if the garbageDeck exists
                garbageDeck.stack(destroy.getHand()); // return hand to deck
            }
            if (destroy.getHand() != null) {
                destroy.getHand().empty();
            }
        }

        // get ready to send JMS message
        checkWinner();
        createMessage();
        message.setLoser(destroy.getUsername());
        message.setKicked(UID);
        try {
            sendJMSMessageToGameStateTopic(message);
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
        message.setWinner(null);
        message.setLoser(null);
        message.setKicked(-1);
        System.out.println(activePlayers);

        // Remove player from players table
        if (players.remove(IntUID).equals(destroy)) {
            return true;
        } else {
            return false;
        }

    }

    /**
     * Returns the leaderboard in the ScoreBoard object format
     * @return ScoreBoard object containing the leaderboard information
     */
    public ScoreBoard getScores() {
        int index, max;
        scores = new ScoreBoard();
        List<Object[]> score = findPlayed();

        max = Math.min(10, score.size());
        for (index = 0; index < 10; index++) {
            if (index < score.size()) {
                scores.getGamesPlayed().add(new Score(
                        (String) score.get(index)[0],
                        ((Integer) score.get(index)[1]).doubleValue()));
            } else {
                scores.getGamesPlayed().add(new Score("", 0));
            }
        }

        score = findWon();
        max = Math.min(10, score.size());
        for (index = 0; index < 10; index++) {
            if (index < score.size()) {
                scores.getGamesWon().add(new Score((String) score.get(index)[0],
                        ((Integer) score.get(index)[1]).doubleValue()));
            } else {
                scores.getGamesWon().add(new Score("", 0));
            }
        }

        score = findRatio();
        max = Math.min(10, score.size());
        for (index = 0; index < 10; index++) {
            if (index < score.size()) {
                scores.getWinRatio().add(new Score(
                        (String) score.get(index)[0],
                        ((Double) score.get(index)[1]).doubleValue()));
            } else {
                scores.getWinRatio().add(new Score("", 0));
            }
        }
        return scores;
    }

    /**
     * Sends a 2D array of Strings containing the players and their information
     *
     * @return 2D array of strings for displaying in the admin client.
     */
    public String[][] getPlayers() {
        DecimalFormat fmt = new DecimalFormat("#.####");
        List<PlayerEntity> playersDB = findAll();
        String playerArray[][] = new String[playersDB.size()][5];

        for (int x = 0; x < playersDB.size(); x++) {
            playerArray[x][0] = Integer.toString(playersDB.get(x).getId());
            playerArray[x][1] = playersDB.get(x).getUsername();
            playerArray[x][2] = Integer.toString(playersDB.get(x).getGameplayed());
            playerArray[x][3] = Integer.toString(playersDB.get(x).getGamewon());
            playerArray[x][4] = fmt.format(playersDB.get(x).getGameratio());
        }
        for (int x = 0; x < playersDB.size(); x++) {
            for (int y = 0; y < 5; y++) {
                System.err.print(playerArray[x][y]);
            }
            System.err.println();
        }

        return playerArray;
    }

    /**
     * Sends a list of online player's usernames
     * @return List of Player objects
     */
    public List<Player> getOnlinePlayers() {
        Player temp = null;
        List<Player> onlinePlayers = new ArrayList<Player>();
        for (Enumeration<Player> e = players.elements(); e.hasMoreElements();) {
            temp = e.nextElement();
            onlinePlayers.add(new Player(temp.getUserid(), temp.getUsername()));
        }
        return onlinePlayers;
    }

    /**
     * Used to set the state for testing purposes
     * @param playing
     * @param CP
     * @param total
     * @param order
     * @param LCP
     * @param count
     * @param turn
     * @param players
     * @param AP
     * @param DD
     * @param GD
     * @param msg
     * @param admin
     * @param results
     * @return
     */
    public boolean setGameState(boolean playing, int CP, int total, int order,
            Card LCP, int count, int turn, Hashtable players, ArrayList AP,
            Deck DD, Deck GD, NinetyNineMessage msg, Player admin,
            PlayerEntity results) {
        this.playing = playing; // boolean game currently being played
        this.currentPlayer = CP; // int current player in activePlayers
        this.total = total; // int running total in game. should never exceed 99
        this.order = order; // int +1 means forwards, -1 means backwards
        this.lastCardPlayed = LCP; // Card object of the most recent card played
        this.count = count; // int counts the number of cards the player has played
        this.turn = turn; // int number of turns that have passed since the game started
        this.players = players; // Hashtable<Integer, Player> logged in players
        this.activePlayers = AP; // ArrayList<Player> list of active players
        this.drawDeck = DD; // Deck deck that players draw from
        this.garbageDeck = GD; // Deck garbage deck, shuffle and add to
        this.message = msg; // NinetyNineMessage Message for JMS
        this.admin = admin; // Player special admin player
        this.results = results; // PlayerEntity Entity object that is used to access DB
        return true;
    }

    // JPA methods
    // flush the persistence context
    private void save() {
        em.flush();
    }

    // create object in persistence context
    private void create(PlayerEntity entity) {
        em.persist(entity);
    }

    // edit an object in the persistence context
    private void edit(PlayerEntity entity) {
        em.merge(entity);
    }

    // remove an object from the persistence context
    private void remove(PlayerEntity entity) {
        em.remove(em.merge(entity));
    }

    // find an object with the matching ID in the database and return it
    private PlayerEntity find(Object id) {
        return em.find(PlayerEntity.class, id);
    }

    // find an object with the matching name in the database and return it
    private PlayerEntity findWithName(String name) {
        try {
            return (PlayerEntity) em.createQuery("SELECT c FROM PlayerEntity c "
                    + "WHERE c.username = :playerName").setParameter(
                    "playerName", name).getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }

    // get the players who've played the most games
    private List<Object[]> findPlayed() {
        return em.createQuery("SELECT c.username, c.gameplayed FROM "
                + "PlayerEntity c WHERE NOT c.id=0 ORDER BY c.gameplayed"
                + " DESC").getResultList();
    }

    // get the players who've won the most games
    private List<Object[]> findWon() {
        return em.createQuery("SELECT c.username, c.gamewon FROM PlayerEntity c"
                + " WHERE NOT c.id=0 ORDER BY c.gamewon DESC").getResultList();
    }

    // get the players who have the highest win ratio
    private List<Object[]> findRatio() {
        return em.createQuery("SELECT c.username, c.gameratio FROM PlayerEntity"
                + " c WHERE NOT c.id=0 ORDER BY c.gameratio"
                + " DESC").getResultList();
    }

    // update the number of games played and win ratio in the db
    private int savePlayed(String name, int played, double ratio) {
        return em.createQuery("UPDATE PlayerEntity c SET c.gameplayed ="
                + " :gamePlayed, c.gameratio = :gameRatio WHERE c.username ="
                + " :playerName").setParameter("gamePlayed", played).setParameter("gameRatio", ratio).setParameter("playerName",
                name).executeUpdate();
    }

    // update the number of wins and win ratio in the db
    private int saveWon(String name, int won, double ratio) {
        return em.createQuery("UPDATE PlayerEntity c SET c.gamewon = :gameWon,"
                + " c.gameratio = :gameRatio WHERE c.username = :playerName").setParameter("gameWon", won).setParameter("gameRatio", ratio).setParameter("playerName", name).executeUpdate();
    }

    // get all the players in the database as a list
    private List<PlayerEntity> findAll() {
        javax.persistence.criteria.CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(PlayerEntity.class));
        return em.createQuery(cq).getResultList();
    }

    // get the range of objects in the database as a list
    private List<PlayerEntity> findRange(int[] range) {
        javax.persistence.criteria.CriteriaQuery cq = em.getCriteriaBuilder().
                createQuery();
        cq.select(cq.from(PlayerEntity.class));
        javax.persistence.Query q = em.createQuery(cq);
        q.setMaxResults(range[1] - range[0]);
        q.setFirstResult(range[0]);
        return q.getResultList();
    }

    // count the number of objects in the database
    private int count() {
        javax.persistence.criteria.CriteriaQuery cq = em.getCriteriaBuilder().
                createQuery();
        javax.persistence.criteria.Root<PlayerEntity> rt =
                cq.from(PlayerEntity.class);
        cq.select(em.getCriteriaBuilder().count(rt));
        javax.persistence.Query q = em.createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }
}
